package com.yjk1.example.myFirstSofawebBoot.framework;

import com.yjk1.example.myFirstSofawebBoot.mapping.RegisterMappingException;
import com.yjk1.example.myFirstSofawebBoot.mapping.RequestMappingRegister;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyProcessorSupport;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.support.ResourceEditorRegistrar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;


import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;

/**
 * @author yujiakui
 * @version 1.0
 * Email: jkyu@haiyi-info.com
 * date: 2018/11/12 14:38
 * description：动态加载jar包服务
 **/
@Component
public class DynamicJarManageServiceImpl implements DynamicJarManageService,
        ApplicationContextAware {

    /**
     * 日志对象
     */
    private final static Logger LOGGER = LoggerFactory.getLogger(DynamicJarManageServiceImpl.class);

    /**
     * key1为插件名称，key2为bean名称,value为bean对象
     */
    private Map<String, Map<String, Object>> bizPluginBeanObjs = new HashMap<>();

    /**
     * 业务插件classLoader map
     */
    private Map<String, ClassLoader> bizPluginClassLoaderMap = new HashMap<>();

    /**
     * key1为插件名称，value为BeanDefinitionHolder
     */
    private Map<String, Set<BeanDefinitionHolder>> beanDefinitionHolderMap = new HashMap<>();

    /**
     * 动态mybatis xml mapper 刷新
     */
    private Map<String, DynamicMybatisMapperRefresh> xmlMapperRefreshMap = new HashMap<>();

    /**
     * key1为pluginName，key2为class 全限定类名,value 为对应的bean名称（如果component有对应的bean别名都放在这里面）
     */
    private Map<String, Map<String, String>> pluginClassBeanNameMap = new HashMap<>();

    /**
     * spring 上下文,获取对应的管理bean对象
     */
    private GenericApplicationContext genericApplicationContext;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * spring 上下文对应的classloader重载标记
     */
    private boolean classLoaderOverrideFlag = false;
    @Autowired
    private RequestMappingRegister requestMappingRegister;

    @Override
    public boolean load(String jarPath, String pluginName, List<String> dependencyPluginNames) {
        if (bizPluginBeanObjs.containsKey(pluginName)) {
            LOGGER.warn("插件pluginName={}已经加载", pluginName);
            return false;
        }
        try {
            synchronized (this) {
                if (!classLoaderOverrideFlag) {
                    ClassLoader contextClassLoader = genericApplicationContext.getDefaultListableBeanFactory().getBeanClassLoader();
                    System.out.println("---------contextClassLoader----"+contextClassLoader.getClass().getName());
                    DynamicFrameworkClassLoader frameworkClassLoader = new DynamicFrameworkClassLoader(contextClassLoader);
                    genericApplicationContext.getDefaultListableBeanFactory().setBeanClassLoader(frameworkClassLoader);
                    classLoaderOverrideFlag = true;
                }
            }
            DynamicJarClassLoader loader = new DynamicJarClassLoader(
                    genericApplicationContext.getClassLoader(), this, dependencyPluginNames);

            List<URL> allJarUrls = null;
            try {
                allJarUrls = findDependencyJar(jarPath);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            for (URL jarUrl : allJarUrls) {
                loader.addURLFile(jarUrl);
            }

            bizPluginClassLoaderMap.put(pluginName, loader);
            // TODO test
            DynamicFrameworkClassLoader tempFrameworkClassLoader
                    = (DynamicFrameworkClassLoader) genericApplicationContext.getDefaultListableBeanFactory().getBeanClassLoader();
            tempFrameworkClassLoader.getDynamicJarClassLoaderMap().put(pluginName, loader);
            LOGGER.info("插件pluginName={}加载成功", pluginName);
            return true;
        } catch (Exception ex) {
            LOGGER.error("加载插件pluginName={}异常ex={}", pluginName, ex);
            return false;
        }
    }


    @Override
    public boolean register(String pluginName, String locationPattern) {
        ClassLoader originClassLoader = genericApplicationContext.getDefaultListableBeanFactory().getBeanClassLoader();
        try {
            if (bizPluginBeanObjs.containsKey(pluginName)) {
                LOGGER.warn("插件pluginName={}已经注册过", pluginName);
                return false;
            }
            ClassLoader urlClassLoader = bizPluginClassLoaderMap.get(pluginName);

            genericApplicationContext.getDefaultListableBeanFactory().setBeanClassLoader(urlClassLoader);
            // mybatis 动态注册
            doMybatisRegist(pluginName, urlClassLoader, locationPattern);

            //获取导入的jar的controller  service  dao 等类，并且创建BeanDefinition
            Set<BeanDefinition> beanDefinitions = null;
            try {
                beanDefinitions = getBeanDefinitions(pluginName, urlClassLoader, locationPattern);
            } catch (Exception e) {
                LOGGER.error("根据locationPattern={}获取的beanDefine异常ex={}", locationPattern, e);
                throw new RuntimeException(e);
            }
            Map<String, String> classBeanNameMap = pluginClassBeanNameMap.get(pluginName);

            beanDefinitions.forEach(item -> {
                String beanClassName = item.getBeanClassName();
                String tempBeanClassName = beanClassName.substring(beanClassName.lastIndexOf(".") + 1);
                String beanName = getBeanNameFromClassName(tempBeanClassName);
                if (!CollectionUtils.isEmpty(classBeanNameMap) &&
                        !StringUtils.isEmpty(classBeanNameMap.get(beanClassName))) {
                    beanName = classBeanNameMap.get(beanClassName);
                }
                System.out.println("beanName=====>" + beanName);
                //根据beanDefinition通过BeanFactory注册bean
                genericApplicationContext.getDefaultListableBeanFactory()
                        .registerBeanDefinition(beanName, item);
            });
            registBeanInContext(urlClassLoader, beanDefinitions, pluginName);

            return true;
        } catch (Exception ex) {
            LOGGER.error("注册插件pluginName={}失败ex={}", pluginName, ex);
            return false;
        } finally {
            genericApplicationContext.getDefaultListableBeanFactory().setBeanClassLoader(originClassLoader);
        }
    }

    /**
     * mybatis 动态注册
     *
     * @param urlClassLoader
     * @param locationPattern
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     * @throws IOException
     */
    private void doMybatisRegist(String pluginName, ClassLoader urlClassLoader, String locationPattern)
            throws IllegalAccessException, NoSuchFieldException, IOException {
        // 注册mybatis plus，扫描对应的mapper文件
        registMybatisPlus(pluginName, locationPattern, urlClassLoader);

        // 注册mybatis plus，扫描对应的mapper.xml文件
        DynamicMybatisMapperRefresh dynamicMybatisMapperRefresh =
                new DynamicMybatisMapperRefresh(
                        new String[]{"classpath*:/mapper/*Mapper*.xml"}, sqlSessionFactory
                        , urlClassLoader);
        this.xmlMapperRefreshMap.put(pluginName, dynamicMybatisMapperRefresh);

        dynamicMybatisMapperRefresh.scanMapperXml();
    }

    /**
     * 注册mybatis plus对应的Mapper接口
     *
     * @param locationPattern
     * @param urlClassLoader
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     * @throws IOException
     */
    private void registMybatisPlus(String pluginName, String locationPattern, ClassLoader urlClassLoader) throws NoSuchFieldException, IllegalAccessException, IOException {
        int sepIndex1 = locationPattern.indexOf(":");
        String scanClassPackages = locationPattern.substring(sepIndex1 + 1);
        sepIndex1 = scanClassPackages.indexOf("*");
        scanClassPackages = scanClassPackages.substring(0, sepIndex1 - 1);

        ResourcePatternResolver resourcePatternResolver =
                new PathMatchingResourcePatternResolver(urlClassLoader);
        /**
         * 将加载mybatis的类加载器装配给spring,否则mapper无法注入到ioc容器中
         */
        genericApplicationContext.getDefaultListableBeanFactory().addPropertyEditorRegistrar(
                new ResourceEditorRegistrar(resourcePatternResolver, new StandardEnvironment()));
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                MybatisMapperScannerRegistrar.doScan(
                        "com.yjk.example.daoDemo.mybatis.mapper",
                        resourcePatternResolver);
        this.beanDefinitionHolderMap.put(pluginName, beanDefinitionHolders);

    }

    /**
     * 根据类名获取对应的bean的名称
     *
     * @param className
     * @return
     */
    private String getBeanNameFromClassName(String className) {
        return className.substring(0, 1).toLowerCase() + className.substring(1);
    }

    /**
     * 注册bean到spring context中
     *
     * @param classLoader
     * @param beanDefinitions
     * @param pluginName
     */
    private void registBeanInContext(ClassLoader classLoader,
                                     Set<BeanDefinition> beanDefinitions, String pluginName) {
        Map<AbstractAutoProxyCreator, ClassLoader> proxyCreatorClassLoaderMap = new HashMap<>();
        try {
            // 清除元数据缓存
            genericApplicationContext.getDefaultListableBeanFactory().clearMetadataCache();
            //获取requestMappingHandlerMapping，用来注册HandlerMapping
            RequestMappingHandlerMapping requestMappingHandlerMapping = genericApplicationContext.getBean(RequestMappingHandlerMapping.class);
            // 处理自动代理创建classLoader
            // TODO test
            handleAutoProxyCreatorClassLoader(classLoader, proxyCreatorClassLoaderMap);

            Map<String, String> classBeanNameMap = pluginClassBeanNameMap.get(pluginName);
            beanDefinitions.forEach(item -> {
                String classname = item.getBeanClassName();
                System.out.println("className-------->" + classname);
                try {
                    Class<?> c = Class.forName(classname, false, classLoader);
                    Object proxy = null;
                    // 不是接口也不是抽象类
                    if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) {

                        String classSimpleName = c.getSimpleName();
                        String beanName = getBeanNameFromClassName(classSimpleName);
                        if (!CollectionUtils.isEmpty(classBeanNameMap)
                                && !StringUtils.isEmpty(classBeanNameMap.get(classname))) {
                            beanName = classBeanNameMap.get(classname);
                        }
                        //获取该bean 真正的创建
                        proxy = genericApplicationContext.getBean(beanName, c);
                        Map<String, Object> jarBeanObjs = bizPluginBeanObjs.get(pluginName);
                        if (null == jarBeanObjs) {
                            jarBeanObjs = new HashMap<>();
                            bizPluginBeanObjs.put(pluginName, jarBeanObjs);
                        }
                        jarBeanObjs.put(beanName, proxy);
                    }
                    // 处理controller注解的情况
                    this.requestMappingRegister.registerForControllerClass(c,proxy,pluginName);
                } catch (ClassNotFoundException | RegisterMappingException e) {
                    e.printStackTrace();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 恢复成原来的classLoader
            for (Map.Entry<AbstractAutoProxyCreator, ClassLoader> ele : proxyCreatorClassLoaderMap.entrySet()) {
                /*ele.getKey().setBeanClassLoader(ele.getValue());*/
                ele.getKey().setBeanClassLoader(
                        genericApplicationContext.getDefaultListableBeanFactory().getBeanClassLoader());
            }
        }
    }

    /**
     * 处理自动代理创建classLoader
     *
     * @param classLoader
     * @param proxyCreatorClassLoaderMap
     * @throws IllegalAccessException
     */
    private void handleAutoProxyCreatorClassLoader(ClassLoader classLoader,
                                                   Map<AbstractAutoProxyCreator, ClassLoader> proxyCreatorClassLoaderMap)
            throws IllegalAccessException {
        Map<String, AbstractAutoProxyCreator> abstractAutoProxyCreatorMap =
                genericApplicationContext.getBeansOfType(AbstractAutoProxyCreator.class);
        for (AbstractAutoProxyCreator abstractAutoProxyCreator : abstractAutoProxyCreatorMap.values()) {
            Field proxClassLoaderField = ReflectionUtils.findField(ProxyProcessorSupport.class, "proxyClassLoader");
            proxClassLoaderField.setAccessible(true);
            ClassLoader proxyClassLoaderObj = (ClassLoader) proxClassLoaderField.get(abstractAutoProxyCreator);
            proxyCreatorClassLoaderMap.put(abstractAutoProxyCreator, proxyClassLoaderObj);
            abstractAutoProxyCreator.setBeanClassLoader(classLoader);
        }
    }

    /**
     * 处理Controller注解的情况
     *
     * @param requestMappingHandlerMapping
     * @param c
     * @param proxy
     */
    private void handleController(RequestMappingHandlerMapping requestMappingHandlerMapping,
                                  Class<?> c, Object proxy) {
        Controller annotation = c.getAnnotation(Controller.class);
        //如果此bean是Controller，则注册到RequestMappingHandlerMapping里面
        if (annotation != null) {

            Method getMappingForMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class,
                    "getMappingForMethod", Method.class, Class.class);
            getMappingForMethod.setAccessible(true);
            try {
                Method[] method_arr = c.getMethods();
                for (Method m_d : method_arr) {
                    if (m_d.getAnnotation(RequestMapping.class) != null) {
                        //创建RequestMappingInfo
                        RequestMappingInfo mapping_info = (RequestMappingInfo) getMappingForMethod.invoke(requestMappingHandlerMapping, m_d, c);
                        //注册
                        requestMappingHandlerMapping.registerMapping(mapping_info, proxy, m_d);
                    }

                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    @Override
    public boolean unRegister(String pluginName) {
        try {
            Map<String, Object> pluginBeanObjMap = bizPluginBeanObjs.get(pluginName);
            if (null != pluginBeanObjMap) {
                for (Map.Entry<String, Object> ele : pluginBeanObjMap.entrySet()) {
                    // 卸载bean:这个移除bean同时也移除对bean的依赖
                    //genericApplicationContext.getDefaultListableBeanFactory().removeBeanDefinition(ele.getKey());
                    // 仅仅删除bean的实例，beanDefine没有删除，则在getbean的时候还会根据beanDefine重新创建对应的bean
                    genericApplicationContext.getDefaultListableBeanFactory().destroySingleton(ele.getKey());
                    removeSingleBeanDefine(genericApplicationContext.getDefaultListableBeanFactory(), ele.getKey());
                    /*BeanDefinitionRegistry factory =
                            (BeanDefinitionRegistry) applicationCtx.getAutowireCapableBeanFactory();*/
                }
                bizPluginBeanObjs.remove(pluginName);
            }

            Set<BeanDefinitionHolder> beanDefinitionHolders = beanDefinitionHolderMap.get(pluginName);
            if (null != beanDefinitionHolders) {
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    genericApplicationContext.getDefaultListableBeanFactory()
                            .removeBeanDefinition(beanDefinitionHolder.getBeanName());

                }
                beanDefinitionHolders.remove(pluginName);
            }

            // 清除
            pluginClassBeanNameMap.remove(pluginName);

            DynamicMybatisMapperRefresh dynamicMybatisMapperRefresh = xmlMapperRefreshMap.get(pluginName);
            if (null != dynamicMybatisMapperRefresh) {
                dynamicMybatisMapperRefresh.clear();
            }

            // 清除元数据缓存
            AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor
                    = genericApplicationContext.getBean(AutowiredAnnotationBeanPostProcessor.class);
            if(null != autowiredAnnotationBeanPostProcessor) {
                Map<String, InjectionMetadata> injectionMetadataCache =
                        (Map<String, InjectionMetadata>) getFieldValue(autowiredAnnotationBeanPostProcessor, "injectionMetadataCache");
                for (Map<String, Object> ele : bizPluginBeanObjs.values()) {
                    for (String tempBeanName : ele.keySet()) {
                        // Quick check on the concurrent map first, with minimal locking.
                        synchronized (injectionMetadataCache) {
                            injectionMetadataCache.remove(tempBeanName);
                        }
                    }
                }
            }

            //清除controller
            requestMappingRegister.unRegisterForPluginName(pluginName);
            return true;
        } catch (Exception ex) {
            LOGGER.error("插件pluginName={}卸载bean失败ex={}", pluginName, ex);
            return false;
        }
    }

    /**
     * 清除单例bean的定义 beanDefine
     *
     * @param defaultListableBeanFactory
     * @param beanName
     */
    private void removeSingleBeanDefine(DefaultListableBeanFactory defaultListableBeanFactory, String beanName) {

        try {
            Map<String, BeanDefinition> beanDefinitionMap = (Map<String, BeanDefinition>) getFieldValue(defaultListableBeanFactory, "beanDefinitionMap");
            BeanDefinition bd = beanDefinitionMap.remove(beanName);
            if (bd == null) {
                LOGGER.error("No bean named '" + beanName + "' found in " + this);
                throw new NoSuchBeanDefinitionException(beanName);
            }

            List<String> beanDefinitionNames = (List<String>) getFieldValue(defaultListableBeanFactory, "beanDefinitionNames");
            // Still in startup registration phase
            beanDefinitionNames.remove(beanName);

            Method clearMergedBeanDefinitionMethod = ReflectionUtils.findMethod(
                    DefaultListableBeanFactory.class, "clearMergedBeanDefinition", String.class);
            clearMergedBeanDefinitionMethod.setAccessible(true);
            ReflectionUtils.invokeJdbcMethod(clearMergedBeanDefinitionMethod, defaultListableBeanFactory, beanName);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 获取对应的字段值
     *
     * @param object
     * @param fieldName
     * @return
     */
    private Object getFieldValue(Object object, String fieldName) {
        Field beanDefinitionNamesField = ReflectionUtils.findField(object.getClass(), fieldName);
        beanDefinitionNamesField.setAccessible(true);
        return ReflectionUtils.getField(beanDefinitionNamesField, object);
    }

    @Override
    public boolean unload(String pluginName) {
        try {
            ClassLoader classLoader = bizPluginClassLoaderMap.remove(pluginName);
            if (classLoader instanceof DynamicJarClassLoader) {
                ((DynamicJarClassLoader) classLoader).unloadJarFiles();
            }
            for (ClassLoader tempClassLoader : bizPluginClassLoaderMap.values()) {
                DynamicJarClassLoader dynamicJarClassLoader = (DynamicJarClassLoader) tempClassLoader;
                List<String> dependencyPluginNames = dynamicJarClassLoader.getDependencyPluginNames();
                if (!CollectionUtils.isEmpty(dependencyPluginNames) && dependencyPluginNames.contains(pluginName)) {
                    dependencyPluginNames.remove(pluginName);
                }
            }
            return true;
        } catch (Exception ex) {
            LOGGER.error("插件pluginName={}classLoader卸载失败ex={}", pluginName, ex);
            return false;
        }
    }

    @Override
    public ClassLoader getPluginClassLoader(String pluginName) {
        return this.bizPluginClassLoaderMap.get(pluginName);
    }

    /**
     * 查找jar包依赖
     *
     * @param pluginPath
     * @return
     * @throws MalformedURLException
     */
    private static List<URL> findDependencyJar(String pluginPath) throws MalformedURLException {
        File file = new File(pluginPath);
        List<URL> list = new ArrayList<>();
        File parentFile = file.getParentFile();
        File libFile = new File(file.getParent() + File.separator + "lib");
        if (libFile.exists() && parentFile.isDirectory()) {
            for (File jar : libFile.listFiles()) {
                LOGGER.info("pluginPath={}依赖的jar包lib-jar={}", pluginPath, jar.getPath());
                if (jar.isFile()
                        && jar.getName().toLowerCase().endsWith(".jar")
                        ) {
                    list.add(new URL("jar:file:/" + jar.getPath() + "!/"));
                }
            }
        }
        list.add(new URL("jar:file:/" + pluginPath + "!/"));
        return list;
    }

    /**
     * 获取指定包下的类对应bean的定义
     * 例如：classpath*:com/yjk/** /*.**
     *
     * @param classLoader
     * @return
     * @throws Exception
     */
    public Set<BeanDefinition> getBeanDefinitions(String pluginName, ClassLoader classLoader, String locationPattern) throws Exception {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();

        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(classLoader);
        Resource[] resources = resourcePatternResolver.getResources(locationPattern);
        MetadataReaderFactory metadata = new SimpleMetadataReaderFactory();
        for (Resource resource : resources) {
            MetadataReader metadataReader = metadata.getMetadataReader(resource);
            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
            AnnotationMetadata metadata1 = sbd.getMetadata();
            if (metadata1.isAbstract() || metadata1.isInterface() || metadata1.isAnnotation() ||
                    ((null != metadata1.getEnclosingClassName() || "" != metadata1.getEnclosingClassName())
                            && !metadata1.isIndependent())) {
                continue;
            }
            sbd.setResource(resource);
            sbd.setSource(resource);
            String classname = sbd.getBeanClassName();
            Component component = AnnotationUtils.findAnnotation(Class.forName(classname, false, classLoader), Component.class);
            if (null != component) {
                candidates.add(sbd);
                if (null != component.value() && !"".equals(component.value())) {
                    Map<String, String> classBeanNameMap = pluginClassBeanNameMap.get(pluginName);
                    if (null == classBeanNameMap) {
                        classBeanNameMap = new HashMap<>();
                        pluginClassBeanNameMap.put(pluginName, classBeanNameMap);
                    }
                    classBeanNameMap.put(classname, component.value());
                }
            }
        }

        return candidates;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (applicationContext instanceof GenericApplicationContext) {
            this.genericApplicationContext = (GenericApplicationContext) applicationContext;
        } else {
            String errMsg = MessageFormat.format(
                    "对应的applicationContext={0}不是类型GenericApplicationContext子类型", applicationContext);
            LOGGER.error("对应的applicationContext={}不是类型GenericApplicationContext子类型", applicationContext);
            throw new RuntimeException(errMsg);
        }
    }

}
